package com.tsd.core.datasource;

import cn.hutool.db.Entity;
import com.alibaba.druid.pool.DruidDataSource;
import com.tsd.core.constants.Constants;
import com.tsd.core.utils.ExceptionUtil;
import com.tsd.core.vo.HlpException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import javax.sql.DataSource;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author Luoyc
 * @Date 2018/12/12 19:27
 * @Version
 * @Description: 保存和创建数据源
 */
@Order(2)
public class DynamicDataSource extends AbstractRoutingDataSource {
    static public boolean ok = true;

    private static final Logger LOGGER = LoggerFactory.getLogger(DynamicDataSource.class);

    // 默认数据源，也就是主库
    protected DruidDataSource masterDataSource;
    // 保存动态创建的数据源
    private static final Map<Object, Object> TARGET_DATA_SOURCE = new ConcurrentHashMap<>();

    public static Map<Object, Object> getTargetDataSource() {
        return TARGET_DATA_SOURCE;
    }

    public static void addDynamicDataSource(String key, DataSource dataSource) {
        if (checkExist(key)) {
            return;
        }
        TARGET_DATA_SOURCE.put(key, dataSource);
    }

    public static boolean checkExist(String key) {
        return TARGET_DATA_SOURCE.containsKey(key);
    }

    @Override
    protected DataSource determineTargetDataSource() {
        // 根据数据库选择方案，拿到要访问的数据库
        String dataSourceName = determineCurrentLookupKey();
        if (DataSourceContextHolder.DEFAULT_ACCOUNT_SET_SID.equals(dataSourceName)) {
            return masterDataSource;
        }
        if (ok) {
            // 根据数据库名字，从已创建的数据库中获取要访问的数据库
            DataSource dataSource = (DataSource) TARGET_DATA_SOURCE.get(dataSourceName);
            if (null == dataSource) {
                // 从已创建的数据库中获取要访问的数据库，如果没有则创建一个
                try {
                    dataSource = this.selectDataSource(dataSourceName);
                } catch (Exception e) {
                    LOGGER.error(ExceptionUtil.getStackTraceFromException(e));
                }
            }
            return dataSource;
        } else {
            return null;
        }
    }

    @Override
    protected String determineCurrentLookupKey() {
        String dataSourceName = DataSourceContextHolder.getDataSourceType();
        if (dataSourceName == null || dataSourceName.equals(DataSourceContextHolder.DEFAULT_ACCOUNT_SET_SID)) {
            // 默认的数据源名字
            dataSourceName = DataSourceContextHolder.DEFAULT_ACCOUNT_SET_SID;
        }
        return dataSourceName;
    }

    private void addTargetDataSource(String key, DataSource dataSource) {
        TARGET_DATA_SOURCE.put(key, dataSource);
        setTargetDataSources(TARGET_DATA_SOURCE);
    }

    /**
     * 该方法为同步方法，防止并发创建两个相同的数据库
     * 使用双检锁的方式，防止并发
     *
     * @param dbType
     * @return
     */
    private synchronized DataSource selectDataSource(String dbType) throws Exception {
        // 再次从数据库中获取，双检锁
        DruidDataSource obj = (DruidDataSource) TARGET_DATA_SOURCE.get(dbType);
        if (null != obj) {
            return obj;
        }
        String threadName = Thread.currentThread().getName();
        System.out.println("DynamicDataSource Thread ID：" + threadName + " dbType:" + dbType);
        // 为空则创建数据库
        DataSource dataSource = this.createDataSource(dbType);
        if (null != dataSource) {
            // 将新创建的数据库保存到map中
            this.setDataSource(dbType, dataSource);
            return dataSource;
        } else {
            throw new Exception("创建数据源失败！");
        }
    }

    /**
     * 创建数据源
     *
     * @param sid
     * @return
     */
    private DataSource createDataSource(String sid) throws Exception {
        String targetType = DataSourceContextHolder.getDataSourceType();
        String dbSid = sid;
        boolean isLog = dbSid.endsWith(Constants.DATABASE_SUFFIX_FOR_LOG);
        if (isLog) {
            dbSid = dbSid.substring(0, dbSid.length() - Constants.DATABASE_SUFFIX_FOR_LOG.length());
        }
        Entity entity = DataSourceUtils.getAccountSetBySid(this.getMasterDataSource(), sid);
        if (entity == null) {
            throw new HlpException("数据库不存在:" + dbSid);
        }
        return DataSourceUtils.newDataSource(entity, isLog);
    }

    private void setDataSource(String type, DataSource dataSource) {
        this.addTargetDataSource(type, dataSource);
        DataSourceContextHolder.setDataSourceType(type);
    }

    @Override
    public void afterPropertiesSet() {
    }

    public DruidDataSource getMasterDataSource() {
        return masterDataSource;
    }

    public void setMasterDataSource(DruidDataSource masterDataSource) {
        this.masterDataSource = masterDataSource;
    }
}
